﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Imaging;
using PersonalWebSite.Models;
using log4net;
using System.Net;
using System.IO;
using System.Xml;
using System.Security.Principal;
using System.Web.Caching;
using System.Web.Script.Serialization;

namespace PersonalWebSite.Common
{
    /// <summary>
    /// Defines external hyperlinks utilities
    /// </summary>
    public class LinksManager
    {
        #region Variables
        //UrlScreenshots
        private static readonly string baseUrlScreenshotsPath = null;
        private const string dataFileName = "Links.json";
        private const string cacheKeyLinksFeed = "LinksFeed";

        private static readonly ILog logger = LogManager.GetLogger(typeof(LinksManager));
        #endregion

        #region Constructor
        /// <summary>
        /// Inits the class
        /// </summary>
        static LinksManager()
        {
            baseUrlScreenshotsPath = HttpContext.Current.Server.MapPath("~/Data/UrlScreenshots");

            log4net.Config.XmlConfigurator.Configure();
        }
        #endregion

        /// <summary>
        /// Takes a screenshot of the specified link
        /// </summary>
        /// <param name="url">URL to take screenshot of</param>
        /// <param name="screenshotSize">capture size</param>
        /// <param name="screenShotCompletedHandlerAction">Handler for when screenshot finishes</param>
        public static void TakeScreenshot(string url, Size screenshotSize, Action<Bitmap> screenShotCompletedHandlerAction)
        {
            if (screenShotCompletedHandlerAction == null)
                return;

            Thread thread = new Thread(new ThreadStart(() => {
                Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
                Panel panelContainer = new Panel { Size = new Size(screenshotSize.Width + 20, screenshotSize.Height + 20), AutoScroll = false };
                WebBrowser webBrowserContainer = new WebBrowser { Dock = DockStyle.Fill, ScrollBarsEnabled = false };
                panelContainer.Controls.Add(webBrowserContainer);
                webBrowserContainer.Navigate(url);
                webBrowserContainer.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler((object sender, WebBrowserDocumentCompletedEventArgs e) =>
                {
                    Bitmap bitmap = new Bitmap(screenshotSize.Width, screenshotSize.Height);
                    panelContainer.DrawToBitmap(bitmap, new Rectangle(new Point(0, 0), screenshotSize));
                    screenShotCompletedHandlerAction(bitmap);
                    bitmap.Dispose();
                });

                while (webBrowserContainer.ReadyState != WebBrowserReadyState.Complete)
                    Application.DoEvents();

                webBrowserContainer.Dispose();
            }));
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }

        /// <summary>
        /// Fetches URL Data
        /// </summary>
        /// <param name="url">URL</param>
        /// <returns>URL Data</returns>
        public static UrlDataViewModel FetchUrlData(string url)
        {
            try
            {
                WebRequest webRequest = HttpWebRequest.Create(url);
                using (StreamReader streamReader = new StreamReader(webRequest.GetResponse().GetResponseStream()))
                {
                    HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
                    htmlDocument.LoadHtml(streamReader.ReadToEnd());

                    var titleNode = htmlDocument.DocumentNode.SelectSingleNode("/html/head/title");
                    string description = null;
                    var descriptionNode = htmlDocument.DocumentNode.SelectSingleNode("/html/head/meta[@name='description']") ??
                        htmlDocument.DocumentNode.SelectSingleNode("/html/head/meta[@type='description']") ??
                        htmlDocument.DocumentNode.SelectSingleNode("/html/head/meta[@property='description']");
                    if (descriptionNode != null)
                        description = descriptionNode.GetAttributeValue("content", null);
                    
                    return new UrlDataViewModel {
                        Title = HttpUtility.HtmlDecode(titleNode.InnerText),
                        Description = HttpUtility.HtmlDecode(description)
                    };
                }
            }
            catch (Exception ex)
            {
                string message = string.Format("An error occured while trying to fetch URL data for \"{0}\"", url);
                logger.Error(message, ex);
                throw;
            }
        }

        /// <summary>
        /// Tries to take a screenshot of the given URL
        /// </summary>
        /// <param name="url">URL</param>
        public static void TryTakeScreenshot(string url, string guid)
        {
            try
            {
                TakeScreenshot(url, new Size(1024, 768), b =>
                {
                    b.Save(baseUrlScreenshotsPath + "\\" + guid + ".png", ImageFormat.Png);
                });
            }
            catch (Exception ex)
            {
                string message = string.Format("An error occured while trying to take a screenshot of the given URL: \"{0}\"", url);
                logger.Error(message, ex);
                throw;
            }
        }

        /// <summary>
        /// Fetches the links
        /// </summary>
        /// <returns>Links Collection</returns>
        public static LinksViewModel FetchLinks()
        {
            return Cache.CacheItem(cacheKeyLinksFeed, () => FetchLinksFromDataFile(), new CacheDependency(HttpContext.Current.Server.MapPath("~/Data/" + dataFileName)), null);
        }

        #region Helper Methods
        /// <summary>
        /// Fetches the videos from the data file
        /// </summary>
        /// <returns>Video Items</returns>
        private static LinksViewModel FetchLinksFromDataFile()
        {
            try
            {
                LinksViewModel model = new JavaScriptSerializer().Deserialize<LinksViewModel>(FileDataManager.LoadFileInfo(dataFileName).Content);
                model.Items = model.Items.OrderByDescending(i => i.Date).ToArray();
                return model;
            }
            catch (Exception ex)
            {
                string message = string.Format("An error occured while trying to fetch the links from data file \"{0}\".", dataFileName);
                logger.Error(message, ex);
                throw;
            }
        }
        #endregion
    }
}